
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sched.h>
#include <assert.h>
#include <signal.h>
#include <time.h>
#include <stdint.h>
#include <unistd.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define COUNTOF(a) (sizeof(a) / sizeof((a)[0]))

#define SPI_DEVICE      "/dev/spidev0.0"
#define SPI_MODE        SPI_MODE_0
#define SPI_DELAY       0
#define SPI_SPEED       20000000
#define SPI_BITS        8

int fd;
	
static void pabort(const char *s)
{
    perror(s);
    abort();
}

static void spi_stransfer(int fd)
{
		int ret;
		uint8_t tx[] = {
			0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 
			0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07,
			0x00, 0x08, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b,
			0x00, 0x0c, 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x0f,
		};
		uint8_t rx[COUNTOF(tx)] = {0};
        #if 0
		struct spi_ioc_transfer tr = {
            .tx_buf         = (unsigned long)tx,
            .rx_buf         = (unsigned long)rx,
            .len            = COUNTOF(tx),
            .delay_usecs    = SPI_DELAY,
            .speed_hz       = SPI_SPEED,
            .bits_per_word  = SPI_BITS,
		};
        #else
        struct spi_ioc_transfer tr = {0};
        tr.tx_buf           = (unsigned long )tx;
        tr.rx_buf           = (unsigned long )rx;
        tr.len              = COUNTOF(tx);
        tr.delay_usecs      = SPI_DELAY;
        tr.speed_hz         = SPI_SPEED;
        tr.bits_per_word    = SPI_BITS;
        #endif 

		ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
		if (ret == 1)
        {
            pabort("can't send spi message");
        }
	
		for (ret = 0; ret < COUNTOF(tx); ret++)
        {
				if (!( ret%6 ))
                {
                    puts("");
                }
				printf("%.2X ", rx[ret]);
		}
		puts("");
}

void spi_timer_cbk()
{
    spi_stransfer(fd);
}

unsigned int spi_timer_create()
{
    int ret;
    timer_t timer;
    struct sigevent evp;
    struct timespec spec;
    struct itimerspec time_value;

    evp.sigev_value.sival_ptr = &timer;
    evp.sigev_notify = SIGEV_SIGNAL; 
    evp.sigev_signo = SIGUSR1;
    signal(SIGUSR1, spi_timer_cbk);

    ret = timer_create(CLOCK_MONOTONIC, &evp, &timer);
    if( ret )
        perror("timer_create");

    #if 1
    time_value.it_interval.tv_sec = 0;                 
    time_value.it_interval.tv_nsec = 1000000; // 
    #else
    time_value.it_interval.tv_sec = 5;                 
    time_value.it_interval.tv_nsec = 0;
    #endif
    clock_gettime(CLOCK_MONOTONIC, &spec);         
    time_value.it_value.tv_sec = spec.tv_sec + 0;     
    time_value.it_value.tv_nsec = spec.tv_nsec + 0;

    ret = timer_settime(timer, CLOCK_MONOTONIC, &time_value, NULL);
    if( ret )
        perror("timer_settime");

    return ret;

}

int spi_create()
{
    int lfd;
    int ret = 0;
    uint8_t bits = 0;
    uint16_t speed = 0;
    uint8_t spi_mode = SPI_MODE;
    char device; 

    lfd = open(SPI_DEVICE, O_RDWR);
	if (lfd < 0)
        pabort("can't open device");

	/* spi mode */
	ret = ioctl(lfd, SPI_IOC_WR_MODE, &spi_mode);
	if (ret == -1)
			pabort("can't set spi mode");
	ret = ioctl(lfd, SPI_IOC_RD_MODE, &spi_mode);
	if (ret == -1)
			pabort("can't get spi mode");

	/* bits per word */
	ret = ioctl(lfd, SPI_IOC_WR_BITS_PER_WORD, &bits);
	if (ret == -1)
			pabort("can't set bits per word");
	ret = ioctl(lfd, SPI_IOC_RD_BITS_PER_WORD, &bits);
	if (ret == -1)
			pabort("can't get bits per word");

	/* max speed hz */
	ret = ioctl(lfd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
	if (ret == -1)
			pabort("can't set max speed hz");
	ret = ioctl(lfd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
	if (ret == -1)
			pabort("can't get max speed hz");

	printf("spi device: %s\n", SPI_DEVICE);
	printf("spi mode: %d\n", SPI_MODE);
	printf("bits per word: %d\n", SPI_BITS);
	printf("max speed: %d Hz (%d KHz)\n", SPI_SPEED, SPI_SPEED/1000);

    return lfd;
}

int main(int argc, char**argv)
{
    int ret = 0;

	fd = spi_create();
    ret = spi_timer_create();

	while(1)
	{
		usleep(1);
	}

	close(fd);

	return ret;
}

